//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//


#include "precomp.h"
//#ifndef NO_STATSTORE
#include "statstor.h"
//#endif // NO_STATSTORE

//
// Table used to convert from GSM default alphabet to Unicode
//
static const WCHAR g_rgwchGSMToUnicode[128] =
{
    L'@',   0x00a3, L'$',   0x00a5, 0x00e8, 0x00e9, 0x00f9, 0x00ec, // 0x00 - 0x07
    0x00f2, 0x00c7, L'\n',  0x00d8, 0x00f8,  L'\r', 0x00c5, 0x00e5, // 0x08 - 0x0f
    0x0394, L'_',   0x03a6, 0x0393, 0x039b, 0x03a9, 0x03a0, 0x03a8, // 0x10 - 0x17
    0x03a3, 0x0398, 0x039e, L' ',   0x00c6, 0x00e6, 0x00df, 0x00c9, // 0x18 - 0x1f
    L' ',   L'!',   L'"',   L'#',   0x00a4, L'%',   L'&',   L'\'',  // 0x20 - 0x27
    L'(',   L')',   L'*',   L'+',   L',',   L'-',   L'.',   L'/',   // 0x28 - 0x2f
    L'0',   L'1',   L'2',   L'3',   L'4',   L'5',   L'6',   L'7',   // 0x30 - 0x37
    L'8',   L'9',   L':',   L';',   L'<',   L'=',   L'>',   L'?',   // 0x38 - 0x3f
    0x00a1, L'A',   L'B',   L'C',   L'D',   L'E',   L'F',   L'G',   // 0x40 - 0x47
    L'H',   L'I',   L'J',   L'K',   L'L',   L'M',   L'N',   L'O',   // 0x48 - 0x4f
    L'P',   L'Q',   L'R',   L'S',   L'T',   L'U',   L'V',   L'W',   // 0x50 - 0x57
    L'X',   L'Y',   L'Z',   0x00c4, 0x00d6, 0x00d1, 0x00dc, 0x00a7, // 0x58 - 0x5f
    0x00bf, L'a',   L'b',   L'c',   L'd',   L'e',   L'f',   L'g',   // 0x60 - 0x67
    L'h',   L'i',   L'j',   L'k',   L'l',   L'm',   L'n',   L'o',   // 0x68 - 0x6f
    L'p',   L'q',   L'r',   L's',   L't',   L'u',   L'v',   L'w',   // 0x70 - 0x77
    L'x',   L'y',   L'z',   0x00e4, 0x00f6, 0x00f1, 0x00fc, 0x00e0, // 0x78 - 0x7f
};


//
// Table used to convert from Unicode to GSM default alphabet
//
static const CHARMAP g_rgcmUnicodeToGSMExceptions[] =
{
    { 0x02, L'$'}, { 0x00, L'@'}, { 0x11, L'_'}, { 0x40, 0x00a1},
    { 0x01, 0x00a3}, { 0x24, 0x00a4}, { 0x03, 0x00a5}, { 0x5f, 0x00a7},
    { 0x60, 0x00bf}, { 0x41, 0x00c0}, { 0x41, 0x00c1}, { 0x41, 0x00c2},
    { 0x41, 0x00c3}, { 0x5b, 0x00c4}, { 0x0e, 0x00c5}, { 0x1c, 0x00c6},
    { 0x09, 0x00c7}, { 0x45, 0x00c8}, { 0x1f, 0x00c9}, { 0x45, 0x00ca},
    { 0x45, 0x00cb}, { 0x49, 0x00cc}, { 0x49, 0x00cd}, { 0x49, 0x00ce},
    { 0x49, 0x00cf}, { 0x5d, 0x00d1}, { 0x4f, 0x00d2}, { 0x4f, 0x00d3},
    { 0x4f, 0x00d4}, { 0x4f, 0x00d5}, { 0x5c, 0x00d6}, { 0x0b, 0x00d8},
    { 0x55, 0x00d9}, { 0x55, 0x00da}, { 0x55, 0x00db}, { 0x5e, 0x00dc},
    { 0x59, 0x00dd}, { 0x1e, 0x00df}, { 0x7f, 0x00e0}, { 0x61, 0x00e1},
    { 0x61, 0x00e2}, { 0x61, 0x00e3}, { 0x7b, 0x00e4}, { 0x0f, 0x00e5},
    { 0x1d, 0x00e6}, { 0x09, 0x00e7}, { 0x04, 0x00e8}, { 0x05, 0x00e9},
    { 0x65, 0x00ea}, { 0x65, 0x00eb}, { 0x07, 0x00ec}, { 0x69, 0x00ed},
    { 0x69, 0x00ee}, { 0x69, 0x00ef}, { 0x7d, 0x00f1}, { 0x08, 0x00f2},
    { 0x6f, 0x00f3}, { 0x6f, 0x00f4}, { 0x6f, 0x00f5}, { 0x7c, 0x00f6},
    { 0x0c, 0x00f8}, { 0x06, 0x00f9}, { 0x75, 0x00fa}, { 0x75, 0x00fb},
    { 0x7e, 0x00fc}, { 0x79, 0x00fd}, { 0x79, 0x00ff}, { 0x13, 0x0393},
    { 0x10, 0x0394}, { 0x19, 0x0398}, { 0x14, 0x039b}, { 0x1a, 0x039e},
    { 0x16, 0x03a0}, { 0x18, 0x03a3}, { 0x12, 0x03a6}, { 0x17, 0x03a8},
    { 0x15, 0x03a9},
};
#define NUM_EXCEPTIONS  (sizeof(g_rgcmUnicodeToGSMExceptions) / sizeof(CHARMAP))


//
// Table used to map semi-byte values to hex characters
//
static const char g_rgchSemiByteToCharMap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

extern DWORD g_dwSIMLockedState;
extern DWORD g_TimeoutCmdInit;


//
// Convert a semi-byte into its character representation
//
char SemiByteToChar(const BYTE bByte, const BOOL fHigh)
{
    BYTE bSemiByte = (fHigh ? (bByte & 0xf0) >> 4 : bByte & 0x0f);
    TBD_ASSERT(0x10 > bSemiByte);
    return g_rgchSemiByteToCharMap[bSemiByte];
}


//
// Combine 2 characters representing semi-bytes into a byte
//
BYTE SemiByteCharsToByte(const char chHigh, const char chLow)
{
    BYTE bRet;

    if ('0' <= chHigh && '9' >= chHigh)
    {
        bRet = (chHigh - '0') << 4;
    }
    else
    {
        bRet = (0x0a + chHigh - 'A') << 4;
    }

    if ('0' <= chLow && '9' >= chLow)
    {
        bRet |= (chLow - '0');
    }
    else
    {
        bRet |= (0x0a + chLow - 'A');
    }
    return bRet;
}


//
// Comparison routine used for binary search below
//
static int _cdecl BSCompareChars(const void* pElem1, const void* pElem2)
{
    WCHAR wch1 = ((CHARMAP*)pElem1)->wch;
    WCHAR wch2 = ((CHARMAP*)pElem2)->wch;
    int iRet;

    if (wch1 < wch2)
    {
        iRet = -1;
    }
    else if (wch1 == wch2)
    {
        iRet = 0;
    }
    else
    {
        iRet = 1;
    }
    return iRet;
}


//
//
//
BOOL FindString(DWORD dwStartPos, char *pszString1, char *pszString2, UINT uiLen, PDWORD pdwEndPos)
{
  DWORD i, j, dwEnd;

  if (pdwEndPos) {
    *pdwEndPos = 0;
  }

  dwEnd = (DWORD)uiLen + dwStartPos;
  for (j = 0, i = dwStartPos; i < dwEnd; i++) {
    if (pszString1[i] == pszString2[j]) {
      j++;
    } else {
      j = 0;
    }
    if (j == strlen(pszString2)) {
      break;
    }
  }

  if (i != dwEnd) {
    if (pdwEndPos) {
      *pdwEndPos = i + 1;
    }
    return TRUE;
  }

  return FALSE;
}

void ExtRILRetailTrace(LPCTSTR szText, LPCTSTR szIn1, LPCSTR szIn2)
{
    TraceCRLFString(szIn2, szText, szIn1);
}

//
// Format a CHAR string into a WCHAR string replacing '\r' and '\n' with visable chars
//
void TraceCRLFString(LPCSTR szIn, LPCTSTR szText, LPCTSTR szIn2) {
#if defined(WANT_RETAIL_RILDRV_OUTPUT) || defined(WANT_FILE_RILDRV_OUTPUT)
  const WCHAR  szCR[] = TEXT("<cr>");
  const WCHAR  szLF[] = TEXT("<lf>");
  WCHAR        szBuf[256];
  UINT         iLenCR = wcslen(szCR);
  UINT         iLenLF = wcslen(szLF);
  UINT         iPrint;
  UINT         i;
  const UINT   iMaxCharsPerLine = 200;

  if (szText==NULL) {
      wcscpy(szBuf, TEXT("RilDrv: "));
  }
  else {
      if (szIn2==NULL)
          wcscpy(szBuf, szText);
      else
          swprintf(szBuf, szText, szIn2);
  }
  iPrint = wcslen(szBuf);
  for (i = 0; i < strlen(szIn); i++) {
    // Check if we have to start a new line
    if (iPrint >= iMaxCharsPerLine) {
      wcscpy(&szBuf[iPrint], TEXT("\r\n"));
      RILRetailTrace((szBuf));
      wcscpy(szBuf, TEXT("RilDrv: ... "));
      iPrint = wcslen(szBuf);
    }

    // Check for chars to be replaced
    if (szIn[i] == '\r') {
      wcscpy(&szBuf[iPrint], szCR);
      iPrint += iLenCR;
    } else if (szIn[i] == '\n') {
      wcscpy(&szBuf[iPrint], szLF);
      iPrint += iLenLF;
    } else {
      szBuf[iPrint] = (WCHAR)szIn[i];
      iPrint++;
      szBuf[iPrint] = TEXT('\0');
    }
  }
  wcscpy(&szBuf[iPrint], TEXT("\r\n"));
  RILRetailTrace((szBuf));
#endif //WANT_RETAIL_RILDRV_OUTPUT
}

//
// Convert a single Unicode character to GSM default alphabet
//
BOOL UnicodeCharToGSM(const WCHAR wch, char *pchRet)
{
    // Does this char map directly to Unicode?
    if (0x000a == wch ||
        0x000d == wch ||
        (0x0020 <= wch && 0x0023 >= wch) ||
        (0x0025 <= wch && 0x003f >= wch) ||
        (0x0041 <= wch && 0x005a >= wch) ||
        (0x0061 <= wch && 0x007a >= wch))
    {
        *pchRet = (char)(wch & 0x007f);
        return TRUE;
    }
    else
    {
        CHARMAP cmKey = { '\0', wch};
        CHARMAP* pcmFound;

        pcmFound = (CHARMAP*)bsearch(&cmKey, g_rgcmUnicodeToGSMExceptions, NUM_EXCEPTIONS, sizeof(CHARMAP), BSCompareChars);
        if (pcmFound)
        {
            *pchRet = pcmFound->ch;
            return TRUE;
        }
    }
    return FALSE;
}

BOOL GSMCharToUnicode(const char ch, WCHAR *pwchRet)
{
    *pwchRet = g_rgwchGSMToUnicode[ch];
    return TRUE;
}

//
//
//
BOOL GSMHexToGSM(const LPCSTR sIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut, UINT& rcbUsed)
{
    LPCSTR pchIn = sIn;
    LPCSTR pchInEnd = sIn + cbIn;
    LPSTR pchOut = sOut;
    LPSTR pchOutEnd = sOut + cbOut;
    BOOL fRet = FALSE;

    while (pchIn < pchInEnd - 1 && pchOut < pchOutEnd)
    {
        *pchOut++ = SemiByteCharsToByte(*pchIn, *(pchIn + 1));
        pchIn += 2;
    }

    rcbUsed = pchOut - sOut;
    fRet = TRUE;
    return fRet;
}


//
//
//
BOOL GSMToGSMHex(const LPCSTR sIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut, UINT& rcbUsed)
{
    LPCSTR pchIn = sIn;
    LPCSTR pchInEnd = sIn + cbIn;
    LPSTR pchOut = sOut;
    LPSTR pchOutEnd = sOut + cbOut;
    BOOL fRet = FALSE;

    while (pchIn < pchInEnd && pchOut < pchOutEnd - 1)
    {
        *pchOut = g_rgchSemiByteToCharMap[((*pchIn) & 0xf0) >> 4];
        pchOut++;
        *pchOut = g_rgchSemiByteToCharMap[(*pchIn) & 0x0f];
        pchOut++;

        pchIn++;
    }

    rcbUsed = pchOut - sOut;
    fRet = TRUE;
    return fRet;
}

//
//
//
BOOL UCS2ToGSMHex(const LPWSTR pwszIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut)
{
    LPWSTR pwchIn = pwszIn;
    LPWSTR pwchInEnd = pwszIn + cbIn;
    LPSTR pchOut = sOut;
    LPSTR pchOutEnd = sOut + cbOut;
    BOOL fRet = FALSE;
    WCHAR wcTemp;

    while (pwchIn < pwchInEnd && pchOut < pchOutEnd - 1)
    {
        wcTemp = *pwchIn++;
        *pchOut = g_rgchSemiByteToCharMap[((wcTemp) & 0xF000) >> 12];
        pchOut++;
        *pchOut = g_rgchSemiByteToCharMap[((wcTemp) & 0x0F00) >> 8];
        pchOut++;
        *pchOut = g_rgchSemiByteToCharMap[((wcTemp) & 0x00F0) >> 4];
        pchOut++;
        *pchOut = g_rgchSemiByteToCharMap[((wcTemp) & 0x000F)];
        pchOut++;
    }
    *pchOut++ = 0;

    fRet = TRUE;
    return fRet;
}

//
// Convert a specified string from Unicode
//
BOOL ConvertFromUnicode(const ENCODING_TYPE enc, const LPCWSTR wsIn, const UINT cchIn, const LPSTR sOut, const UINT cbOut,
                        UINT& rcbUsed)
{
    TBD_FUNCTION(ConvertFromUnicode);
    LPCWSTR pwchInEnd;
    LPCWSTR pwchIn;
    LPSTR pchOutEnd;
    LPSTR pchOut;
    LPSTR sOutTemp = NULL;
    LPSTR pchOutTemp;
    UINT cbOutUsed;
    char ch7Bit;
    UINT nBits = 0;
    BOOL fRet = FALSE;

    rcbUsed = 0;

    if (ENCODING_GSMDEFAULT == enc)
    {
        pwchInEnd = wsIn + cchIn;
        pchOutEnd = sOut + cbOut;
        pwchIn = wsIn;
        pchOut = sOut;

        while (pwchIn < pwchInEnd && pchOut < pchOutEnd)
        {
            if (!nBits)
            {
                if (!UnicodeCharToGSM(*pwchIn++,pchOut))
                {
                    *pchOut = '?';
                }
                TBD_ASSERT(*pchOut < 0x80);
            }
            else
            {
                if (!UnicodeCharToGSM(*pwchIn++,&ch7Bit))
                {
                    ch7Bit = '?';
                }
                TBD_ASSERT(ch7Bit < 0x80);
                *pchOut++ += ch7Bit << (8 - nBits);
                *pchOut    = ch7Bit >> nBits;
            }
            nBits = (nBits + 1) % 8;
        }

        if (!nBits && pchOut > sOut)
        {
            // We incremented the destination pointer once too many
            pchOut--;
        }

        // Do various tricks to distinguish fill bits from real characters (GSM 03.38, ver. 6.0.1, chap. 6.1.2.3.1)
        // NOTE: <CR> in GSM default alphabet maps directly to Unicode <CR>
        if (7 == cchIn % 8 && !(*pchOut & 0xfe))
        {
            // We have 7 fill 0-bits at the end -- need to replace them with <CR>
            *pchOut &= 0x01;
            *pchOut |= ('\r' << 1);
        }
        else if (!(cchIn % 8) && ('\r' << 1) == *pchOut && pchOut < pchOutEnd - 1)
        {
            // We have no fill bits and the last character in the text is <CR> --
            //    we need to add another <CR>, so that terminating <CR> isn't confused with fill bits
            pchOut++;
            *pchOut = '\r' & 0x7f;
        }

        rcbUsed = pchOut - sOut + 1;
        fRet = TRUE;
    }
    else if (ENCODING_GSMDEFAULT_HEX == enc)
    {
        sOutTemp = new char[cbOut / 2 + 1];
        if (!sOutTemp)
        {
            goto Error;
        }
        if (!ConvertFromUnicode(ENCODING_GSMDEFAULT, wsIn, cchIn, sOutTemp, cbOut / 2 + 1, cbOutUsed))
        {
            goto Error;
        }

        pchOutTemp = sOutTemp;
        pchOut = sOut;
        pchOutEnd = sOut + cbOut;

        while (pchOut <= pchOutEnd)
        {
            *pchOut++ = SemiByteToChar(*pchOutTemp, TRUE);
            *pchOut++ = SemiByteToChar(*pchOutTemp++, FALSE);
        }
        fRet = TRUE;
    }
    else if (ENCODING_UCS2 == enc)
    {
        for (UINT i = 0; (rcbUsed < (cbOut - 4)) && (i < cchIn) && wsIn[i]; i++, rcbUsed += 4)
        {
            _snprintf(&(sOut[rcbUsed]), 4, "%0.4X", wsIn[i]);
        }
        if (rcbUsed < cbOut)
        {
            sOut[rcbUsed] = '\0';
            rcbUsed++;
        }
        fRet = TRUE;
    }
    else if (ENCODING_ASCII == enc)
    {
        pwchInEnd = wsIn + cchIn;
        pchOutEnd = sOut + cbOut;
        pwchIn = wsIn;
        pchOut = sOut;

        while (pwchIn < pwchInEnd && pchOut < pchOutEnd)
        {
            if (!UnicodeCharToGSM(*pwchIn++,pchOut))
            {
                *pchOut = '?';
            }
            pchOut++;
        }
        *pchOut = '\0';
        fRet = TRUE;
    }

    Error:
    delete[] sOutTemp;
    return fRet;
}


//
// Convert a specified string to Unicode
//
BOOL ConvertToUnicode(const ENCODING_TYPE enc, const LPCSTR sIn, const UINT cbIn, const LPWSTR wsOut, const UINT cchOut,
                      UINT& rcchUsed)
{
    TBD_FUNCTION(ConvertToUnicode);
    LPCSTR pchInEnd;
    LPCSTR pchIn;
    LPWSTR pwchOutEnd;
    LPWSTR pwchOut;
    LPSTR sInTemp = NULL;
    UINT cbUsedTemp;
    char ch7Bit;
    UINT nBits;
    BYTE bValue;
    BOOL fRet = FALSE;

    rcchUsed = 0;

    if (ENCODING_GSMDEFAULT == enc)
    {
        pchInEnd = sIn + cbIn;
        pwchOutEnd = wsOut + cchOut;
        pchIn = sIn;
        pwchOut = wsOut;
        ch7Bit = 0x00;
        nBits = 0;

        while (pchIn < pchInEnd && pwchOut < pwchOutEnd)
        {
            bValue = (BYTE)*pchIn++;

            ch7Bit += (bValue << nBits) & 0x7f;
            TBD_ASSERT(ch7Bit < 0x80);
            *pwchOut++ = g_rgwchGSMToUnicode[ch7Bit];

            ch7Bit = (bValue >> (7 - nBits)) & 0x7f;
            nBits = (nBits + 1) % 7;
            if (!nBits && pwchOut < pwchOutEnd)
            {
                *pwchOut++ = g_rgwchGSMToUnicode[ch7Bit];
                ch7Bit = 0x00;
            }
        }

        rcchUsed = pwchOut - wsOut;
        fRet = TRUE;
    }
    else if (ENCODING_GSMDEFAULT_HEX == enc)
    {
        sInTemp = new char[cbIn / 2 + 1];
        if (!sInTemp)
        {
            goto Error;
        }

        if (!GSMHexToGSM(sIn, cbIn, sInTemp, cbIn / 2 + 1, cbUsedTemp))
        {
            goto Error;
        }
        if (!ConvertToUnicode(ENCODING_GSMDEFAULT, sInTemp, cbUsedTemp, wsOut, cchOut, rcchUsed))
        {
            goto Error;
        }
        fRet = TRUE;
    }

    Error:
    delete[] sInTemp;
    return fRet;
}


//
// Convert a RILADDRESS structure to AT address string
//
HRESULT RILAddressToString(const RILADDRESS& rraAddress, const LPSTR szOut, const UINT cbOut, BYTE& rbTypeOfAddress)
{
    TBD_FUNCTION(RILAddressToString);
    UINT cbUsed;
    HRESULT hr = E_INVALIDARG;

    // Must have a type and an address specified
    if (!(rraAddress.dwParams & RIL_PARAM_A_TYPE) || !(rraAddress.dwParams & RIL_PARAM_A_ADDRESS))
    {
        goto Error;
    }

    if (rraAddress.dwType >= NUM_ADDRTYPES)
    {
        goto Error;
    }

    if (RIL_ADDRTYPE_ALPHANUM == rraAddress.dwType)
    {
        // Need to convert to default GSM alphabet
        if (!ConvertFromUnicode(ENCODING_GSMDEFAULT_HEX,
                                rraAddress.wszAddress,
                                wcslen(rraAddress.wszAddress),
                                szOut, cbOut - 1, cbUsed))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        // Just copy the number directly
        cbUsed = min(cbOut - 1, wcslen(rraAddress.wszAddress));
        strncpy(szOut, USAsciiString(rraAddress.wszAddress), cbUsed);
    }
    szOut[cbUsed] = '\0';

    // Add address type to Type-of-Address byte (high bit is always set)
    rbTypeOfAddress = 0x80 | (g_rgbAddrTypes[rraAddress.dwType] << 4);

    if (RIL_ADDRTYPE_UNKNOWN == rraAddress.dwType ||
        RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType ||
        RIL_ADDRTYPE_NATIONAL == rraAddress.dwType)
    {
        // Must have a numbering plan specified for these address types
        if (!(rraAddress.dwParams & RIL_PARAM_A_NUMPLAN))
        {
            goto Error;
        }

        if (rraAddress.dwNumPlan >= NUM_NUMPLANS)
        {
            goto Error;
        }

        // Add numbering plan to the Type-of-addres byte
        rbTypeOfAddress |= g_rgbNumPlans[rraAddress.dwNumPlan];
    }
    else
    {
        goto Error;
    }

    hr=S_OK;
    Error:
    return hr;
}

//
// Convert a RILADDRESS structure to a UCS2 AT address string
//
HRESULT RILAddressToUCS2String(const RILADDRESS& rraAddress, const LPWSTR wszOut, const UINT cbOut, BYTE& rbTypeOfAddress)
{
    TBD_FUNCTION(RILAddressToUCS2String);
    HRESULT hr = E_INVALIDARG;

    // Must have a type and an address specified
    if (!(rraAddress.dwParams & RIL_PARAM_A_TYPE) || !(rraAddress.dwParams & RIL_PARAM_A_ADDRESS))
    {
        goto Error;
    }

    if (rraAddress.dwType >= NUM_ADDRTYPES)
    {
        goto Error;
    }

    wcscpy(wszOut, rraAddress.wszAddress);

    // Add address type to Type-of-Address byte (high bit is always set)
    rbTypeOfAddress = 0x80 | (g_rgbAddrTypes[rraAddress.dwType] << 4);

    if (RIL_ADDRTYPE_UNKNOWN == rraAddress.dwType ||
        RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType ||
        RIL_ADDRTYPE_NATIONAL == rraAddress.dwType)
    {
        // Must have a numbering plan specified for these address types
        if (!(rraAddress.dwParams & RIL_PARAM_A_NUMPLAN))
        {
            goto Error;
        }

        if (rraAddress.dwNumPlan >= NUM_NUMPLANS)
        {
            goto Error;
        }

        // Add numbering plan to the Type-of-addres byte
        rbTypeOfAddress |= g_rgbNumPlans[rraAddress.dwNumPlan];
    }
    else
    {
        goto Error;
    }

    hr=S_OK;
    Error:
    return hr;
}

//
// Convert an AT address string to RILADDRESS structure
//
BOOL StringToRILAddress(const LPCSTR szAddress, const BYTE bTypeOfAddress, RILADDRESS& rraAddress)
{
    TBD_FUNCTION(StringToRILAddress);
    BYTE bType = (bTypeOfAddress & 0x70) >> 4;
    BYTE bNumPlan = bTypeOfAddress & 0x0f;
    UINT cchUsed;
    UINT i;
    BOOL fRet = FALSE;

    rraAddress.cbSize = sizeof(RILADDRESS);
    rraAddress.dwParams = 0;

    // Determine the type
    for (i = 0; i < NUM_ADDRTYPES; i++)
    {
        if (g_rgbAddrTypes[i] == bType)
        {
            rraAddress.dwType = i;
            break;
        }
    }
    if (i < NUM_ADDRTYPES)
    {
        rraAddress.dwParams |= RIL_PARAM_A_TYPE;
    }

    if (RIL_ADDRTYPE_UNKNOWN == rraAddress.dwType ||
        RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType ||
        RIL_ADDRTYPE_NATIONAL == rraAddress.dwType)
    {
        // Determine the numbering plan
        for (i = 0; i < NUM_NUMPLANS; i++)
        {
            if (g_rgbNumPlans[i] == bNumPlan)
            {
                rraAddress.dwNumPlan = i;
                break;
            }
        }
        if (i < NUM_NUMPLANS)
        {
            rraAddress.dwParams |= RIL_PARAM_A_NUMPLAN;
        }
    }

    // Convert the address string to Unicode
    if (RIL_ADDRTYPE_ALPHANUM == rraAddress.dwType)
    {
        if (!ConvertToUnicode(ENCODING_GSMDEFAULT_HEX, szAddress, strlen(szAddress), rraAddress.wszAddress,
                              MAXLENGTH_ADDRESS - 1, cchUsed))
        {
            goto Error;
        }
    }
    else
    {
        LPCSTR sz = szAddress;

        // In case we have a '+' in a number that we already
        // know is an INTERNATIONAL type, then remove it.
        if (RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType &&
            '+' == *sz)
        {
            sz++;
        }

        // Just copy the number directly
        cchUsed = min(MAXLENGTH_ADDRESS - 1, strlen(sz));
        wcsncpy(rraAddress.wszAddress, WideString(sz), cchUsed);
    }
    rraAddress.wszAddress[cchUsed] = L'\0';
    rraAddress.dwParams |= RIL_PARAM_A_ADDRESS;
    fRet = TRUE;

    Error:
    return fRet;
}

//
// Convert a UCS2 AT address string to RILADDRESS structure
//
BOOL UCS2StringToRILAddress(const LPWSTR wszAddress, const BYTE bTypeOfAddress, RILADDRESS& rraAddress)
{
    TBD_FUNCTION(UCS2StringToRILAddress);
    BYTE bType = (bTypeOfAddress & 0x70) >> 4;
    BYTE bNumPlan = bTypeOfAddress & 0x0f;
    UINT i;
    BOOL fRet = FALSE;

    rraAddress.cbSize = sizeof(RILADDRESS);
    rraAddress.dwParams = 0;

    // Determine the type
    for (i = 0; i < NUM_ADDRTYPES; i++)
    {
        if (g_rgbAddrTypes[i] == bType)
        {
            rraAddress.dwType = i;
            break;
        }
    }
    if (i < NUM_ADDRTYPES)
    {
        rraAddress.dwParams |= RIL_PARAM_A_TYPE;
    }

    if (RIL_ADDRTYPE_UNKNOWN == rraAddress.dwType ||
        RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType ||
        RIL_ADDRTYPE_NATIONAL == rraAddress.dwType)
    {
        // Determine the numbering plan
        for (i = 0; i < NUM_NUMPLANS; i++)
        {
            if (g_rgbNumPlans[i] == bNumPlan)
            {
                rraAddress.dwNumPlan = i;
                break;
            }
        }
        if (i < NUM_NUMPLANS)
        {
            rraAddress.dwParams |= RIL_PARAM_A_NUMPLAN;
        }
    }

    LPWSTR wsz = wszAddress;

    // In case we have a '+' in a number that we already
    // know is an INTERNATIONAL type, then remove it.
    if (RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType &&
        '+' == *wsz)
    {
        wsz++;
    }

    // Just copy the number directly
    wcscpy(rraAddress.wszAddress, wsz);

    rraAddress.dwParams |= RIL_PARAM_A_ADDRESS;
    fRet = TRUE;

//    Error:
    return fRet;
}

//
// Convert a RILSUBADDRESS structure to AT sub-address string
//
HRESULT RILSubAddressToString(const RILSUBADDRESS& rrsaSubAddress, const LPSTR szOut, const UINT cbOut, BYTE& rbType)
{
    TBD_FUNCTION(RILSubAddressToString);
    HRESULT hr = S_OK;

    // Must have a type and a sub-address specified
    if (!(rrsaSubAddress.dwParams & RIL_PARAM_SA_TYPE) || !(rrsaSubAddress.dwParams & RIL_PARAM_SA_SUBADDRESS))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Copy the subaddress data out of the structure.
    (void)strncpyz(szOut, USAsciiString(rrsaSubAddress.wszSubAddress), cbOut);

    TBD_ASSERT(0x100 > rrsaSubAddress.dwType);
    rbType = (BYTE)rrsaSubAddress.dwType;

    Error:
    return hr;
}


//
// Convert an AT sub-address string to RILSUBADDRESS structure
//
BOOL StringToRILSubAddress(const LPCSTR szSubAddress, const BYTE bType, RILSUBADDRESS& rrsaSubAddress)
{
    TBD_FUNCTION(StringToRILSubAddress);
    rrsaSubAddress.cbSize = sizeof(RILSUBADDRESS);
    rrsaSubAddress.dwParams = 0;

    // Copy the subaddress data into the structure.
    (void)wcsncpyz(rrsaSubAddress.wszSubAddress, WideString(szSubAddress), MAXLENGTH_SUBADDR);
    rrsaSubAddress.dwParams |= RIL_PARAM_SA_SUBADDRESS;

    rrsaSubAddress.dwType = bType;
    rrsaSubAddress.dwParams |= RIL_PARAM_SA_TYPE;
    return TRUE;
}


//
// Determine the error from a SendRestrictedSimCmd based on status word 1 and status word 2
//
HRESULT DetermineSimResponseError(DWORD dwSW1, DWORD dwSW2)
{
    TBD_FUNCTION(DetermineSimResponseError);
    HRESULT hr;

    switch (dwSW1)
    {
    case 0x90:
    case 0x91:
    case 0x9F:
        // This is good
        hr = S_OK;
        break;

    case 0x92:
        if ((dwSW2 & 0xf0) == 0x00)
        {
            // The command completed OK, but the SIM had to retry the command internally
            // a few times, but what do I care if the SIM is whining?
            hr = S_OK;
        }
        else
        {
            // Memory problem
            hr = RIL_E_MEMORYFULL;
        }
        break;

    case 0x94:
        // SW2 = 0x00, 0x02, or 0x08
        // These all means that there was a problem with the file (e.g. invalid address, or
        // they specified the type of file incorrectly)
        hr = RIL_E_INVALIDINDEX;
        break;

    case 0x98:
        // Security and/or authentication problems
        hr = RIL_E_SECURITYFAILURE;
        break;

    case 0x6F:
        // Internal SIM error, no further information given
        hr = RIL_E_SIMFAILURE;
        break;

    case 0x67:
    case 0x6B:
    case 0x6D:
    case 0x6E:
        // I really shouldn't be getting these -- it means that one of the parameters I sent
        // down to the SIM was invalid, but I already error checked for those -- so I guess
        // it's best to assert and let a tester yell at me.  In other words, FALL THROUGH!
        default:
        // I don't recognize this command!
        hr = E_FAIL;
        break;
    }

    return hr;
}


//
// Determines whether a file ID is an elementary SIM file or not
//
BOOL IsElementarySimFile(DWORD dwFileID)
{
    TBD_FUNCTION(IsElementarySimFile);
    BOOL fRetVal = TRUE;
    BYTE bIdentifier;

    // Only the low WORD should be set
    if (dwFileID & 0xffff0000)
    {
        fRetVal = FALSE;
    }
    else
    {
        bIdentifier = (BYTE) ((dwFileID & 0xff00) >> 8);
        if ((bIdentifier != 0x2f) && (bIdentifier != 0x4f) && (bIdentifier != 0x6f))
        {
            // Bogus!  It's not an elementary file
            fRetVal = FALSE;
        }
    }

    return fRetVal;
}

void UpdateStatStore()
{
#ifndef NO_STATSTORE
//  NKDbgPrintfW(TEXT("RilDrv: StatStore = %x\r\n"), g_dwReadyState);
    IStatStore *pss;
    DWORD dwStatStore = 0;

    // Now, update StatStor
    if (SUCCEEDED(GetStatStore(&pss)))
    {
        if (g_dwReadyState & RIL_READYSTATE_INITIALIZED)
        {
            dwStatStore |= RRSF_READYSTATE_INITIALIZED;
        }
        if (g_dwReadyState & RIL_READYSTATE_SIM)
        {
            dwStatStore |= RRSF_READYSTATE_SIM;
        }
        if (g_dwReadyState & RIL_READYSTATE_SMS)
        {
            dwStatStore |= RRSF_READYSTATE_SMS;
        }
        if (g_dwReadyState & RIL_READYSTATE_UNLOCKED)
        {
            dwStatStore |= RRSF_READYSTATE_UNLOCKED;
        }
        if (g_dwReadyState & RIL_READYSTATE_SIM_PB)
        {
            dwStatStore |= RRSF_READYSTATE_SIM_PB;
        }
        pss->UpdateUINT(SDID_RADIO_READY_STATE, FALSE, dwStatStore, RRSF_READYSTATE_ALL);
        pss->Release();
    }
#endif // NO_STATSTORE
}

//
// Update the SIM state
//
void UpdateSIMState(DWORD dwSimState)
{
//  NKDbgPrintfW(TEXT("RilDrv: SimState = %x\r\n"), dwSimState);
//  NKDbgPrintfW(TEXT("RilDrv: StatStore = %x\r\n"), g_dwReadyState);
#ifndef NO_STATSTORE
    IStatStore *pss;
#endif
    DWORD dwStatStoreSimState = 0;
#ifdef SIEMENS_MAGNETO
    DWORD dwSimStatusChanged = 0;
#endif

    // Set this in our global
    switch(dwSimState)
    {
        case RIL_E_SIMPUKREQUIRED:
        case RIL_E_PHFSIMPUKREQUIRED:
        case RIL_E_NETWKPUKREQUIRED:
        case RIL_E_SUBSETPUKREQUIRED:
        case RIL_E_SVCPUKREQUIRED:
        case RIL_E_CORPPUKREQUIRED:
        case RIL_LOCKEDSTATE_SIM_PUK:
        case RIL_LOCKEDSTATE_PH_FSIM_PUK:
        case RIL_LOCKEDSTATE_PH_NET_PUK:
        case RIL_LOCKEDSTATE_PH_NETSUB_PUK:
        case RIL_LOCKEDSTATE_PH_SP_PUK:
        case RIL_LOCKEDSTATE_PH_CORP_PUK:
            dwStatStoreSimState = PSF_BLOCKED_SIM;
#ifdef SIEMENS_MAGNETO
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_BLOCKED;
#endif
           break;

        case RIL_E_SIMWRONG:
            dwStatStoreSimState = PSF_INVALID_SIM;
#ifdef SIEMENS_MAGNETO
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_INVALID;
#endif
            break;

        case RIL_E_SIMNOTINSERTED:
            dwStatStoreSimState = PSF_NO_SIM;
#ifdef SIEMENS_MAGNETO
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_NO_SIM;
#endif
            break;

        case RIL_E_MEMORYFULL:
            dwStatStoreSimState = PSF_SIM_FULL;
#ifdef SIEMENS_MAGNETO
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_FULL;
#endif
            break;

        case RIL_E_SIMPINREQUIRED:
        case RIL_E_PHSIMPINREQUIRED:
        case RIL_E_PHFSIMPINREQUIRED:
        case RIL_E_NETWKPINREQUIRED:
        case RIL_E_SUBSETPINREQUIRED:
        case RIL_E_SVCPINREQUIRED:
        case RIL_E_CORPPINREQUIRED:
        case RIL_E_RADIOOFF:
        case RIL_LOCKEDSTATE_SIM_PIN:
        case RIL_LOCKEDSTATE_PH_SIM_PIN:
        case RIL_LOCKEDSTATE_PH_FSIM_PIN:
        case RIL_LOCKEDSTATE_PH_NET_PIN:
        case RIL_LOCKEDSTATE_PH_NETSUB_PIN:
        case RIL_LOCKEDSTATE_PH_SP_PIN:
        case RIL_LOCKEDSTATE_PH_CORP_PIN:
        case RIL_LOCKEDSTATE_READY:
            // not puk'd, invalid, no sim, or full
            dwStatStoreSimState = 0;
#ifdef SIEMENS_MAGNETO
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_NONE;
#endif
            break;

        case RIL_E_SIMFAILURE:
        case RIL_E_SIMBUSY:
        default:
            // who knows.  could still be puk'd, invalid, no sim, or full
            goto Exit;
            break;
    }

#ifndef NO_STATSTORE
    if (SUCCEEDED(GetStatStore(&pss)))
    {
        pss->UpdateUINT(SDID_PHONE_STATUS_FLAGS, FALSE, dwStatStoreSimState, PSF_SIM);
        pss->Release();
    }
#endif // NO_STATSTORE

#ifdef SIEMENS_MAGNETO
    // Broadcast a SIM state change notification
    CNotificationData* pnd = new CNotificationData;
    if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_SIMSTATUSCHANGED, (void*)&dwSimStatusChanged, sizeof(dwSimStatusChanged)))
    {
        delete pnd;
        pnd = NULL;
    }
    else
    {
        // Add notification to command queue.  pnd will be deleted after notification is sent.
        QueueInternalCmd(URC_PORT, NULL, NULL, CMDOPT_INIT | CMDOPT_NOOP | CMDOPT_IGNORERSP, APIID_NONE, g_TimeoutCmdInit, 0, 0, NULL, NULL, NULL, pnd);
    }
#endif

Exit:
    ;

    if (dwSimState >= RIL_LOCKEDSTATE_UNKNOWN && dwSimState <= RIL_LOCKEDSTATE_PH_CORP_PUK &&
        dwSimState != RIL_LOCKEDSTATE_SIM_PIN2 && dwSimState != RIL_LOCKEDSTATE_SIM_PUK2)
        g_dwSIMLockedState = dwSimState;
}

//
// Creates a cmd string that includes a byte array (in HEX representation)
// NOTE: the caller is responsible for releasing memory allocated for rszCmd
//
BOOL ComposeCmdWithByteArray(const LPCSTR szPrefix, const BYTE* const pbBytes, const UINT cbBytes,
                             const LPCSTR szPostfix, LPSTR& rszCmd)
{
    TBD_FUNCTION(ComposeCmdWithByteArray);
    TBD_ASSERT(NULL != szPrefix);
    TBD_ASSERT(NULL != szPostfix);

    UINT i;
    UINT cbCmd;
    LPSTR szWalk;
    BYTE* pbBytesWalk;
    BOOL fRet = FALSE;

    rszCmd = 0;

    // Allocate the command string
    cbCmd = MAX_ATCMD_LEN + strlen(szPrefix) + strlen(szPostfix) + cbBytes * 2;
    rszCmd = new char[cbCmd];
    if (!rszCmd)
    {
        goto Error;
    }
    szWalk = rszCmd;

    // Add "<prefix>"
    (void)strncpyz(szWalk, szPrefix, cbCmd - (szWalk - rszCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    // Add the byte array coverted to HEX representation, if provided
    if (cbBytes)
    {
        pbBytesWalk = (BYTE*)pbBytes;
        for (i = 0; i < cbBytes; i++)
        {
            TBD_ASSERT(cbCmd >= (UINT)(szWalk - rszCmd + 2));

            *szWalk++ = SemiByteToChar(*pbBytesWalk,   TRUE);
            *szWalk++ = SemiByteToChar(*pbBytesWalk++, FALSE);
        }
    }

    // Add "<postfix>"
    (void)strncpyz(szWalk, szPostfix, cbCmd - (szWalk - rszCmd));  // NO_TYPO: 30
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete[] rszCmd;
    }
    return fRet;
}


//
// Alternate line support
//
// The following function helps us separate the implementation of
// alternate line support without cluttering the original
// source code too much.
//

#define UNUSED_PARAM(x) x

//
// Fills the command buffer with AT plus any required modifier to specify
// the line on which the command should apply.
//
// Returns a pointer to where the rest of the command can be written
// to the buffer.
//
char * BeginLineSpecificCommand(char *szCmdDst, UINT cchSize, DWORD dwAddressId)
{
    UNUSED_PARAM(dwAddressId);
    (void)strncpyz(szCmdDst, "AT", cchSize);
    return strchr(szCmdDst, '\0');  // NO_TYPO: 27
}




//
// (Re)Allocates storage for data elements
//      prgrData - Pointer to current buffer (buffer must be NULL if (0 == nAllocated))
//        stSize - Size (in bytes) of the data elements in the buffer
//         nUsed - Number of valid data elements in the buffer
//   pnAllocated - Total number of data elements in the buffer
//     nGrowSize - Number of elements by which to increase the size of the buffer
//
BOOL AllocateOrReallocateStorage(BYTE** const prgrData, const size_t stSize, const UINT nUsed, UINT* const pnAllocated, const UINT nGrowSize)
{
    TBD_FUNCTION(AllocateOrReallocateStorage);
    TBD_ASSERT(prgrData && pnAllocated && (*prgrData || (0 == *pnAllocated)));
    BOOL fSuccess = FALSE;
    *pnAllocated += nGrowSize;
    const size_t stAllocateBytes = *pnAllocated * stSize;
    BYTE* const prbTemp = (BYTE*)AllocBlob(stAllocateBytes);
    if (prbTemp)
    {
        const size_t stUsedBytes = nUsed * stSize;
        memcpy(prbTemp, *prgrData, stUsedBytes);
        // The following line seems like a good idea, but may waste resources
        // 0-initializing memory that is never used.
        // memset(prbTemp + stUsedBytes, 0x00, stAllocateBytes - stUsedBytes);
        FreeBlob(*prgrData);
        *prgrData = prbTemp;
        fSuccess = TRUE;
    }
    else
    {
        *pnAllocated -= nGrowSize;
    }
    return fSuccess;
}


//
// Buffer ctor
//
CBuffer::CBuffer()
: m_szData(NULL),
m_cbLength(0),
m_cbAlloc(0)
{
    // TBD_FUNCTION(CBuffer::CBuffer);
}


//
// Buffer dtor
//
CBuffer::~CBuffer()
{
    // TBD_FUNCTION(CBuffer::~CBuffer);
    delete[] m_szData;
}


//
// Append data to the response
//
BOOL CBuffer::Append(const LPCSTR szString, const UINT cbString)
{
    // TBD_FUNCTION(CBuffer::Append);
    LPSTR szTemp = NULL;
    BOOL fRet = FALSE;

    if (!m_szData)
    {
        m_cbLength = 0;
        m_cbAlloc = ALLOC_SIZE;
        m_szData = new char[m_cbAlloc];
        if (!m_szData)
        {
            goto Error;
        }
    }

    if (m_cbLength + cbString + 1 > m_cbAlloc)
    {
        do
        {
            m_cbAlloc += ALLOC_SIZE;
        } while (m_cbLength + cbString + 1 > m_cbAlloc);

        szTemp = new char[m_cbAlloc];
        if (!szTemp)
        {
            goto Error;
        }

        memcpy(szTemp, m_szData, m_cbLength);
        delete[] m_szData;
        m_szData = szTemp;
    }

    memcpy(m_szData + m_cbLength, szString, cbString);
    m_cbLength += cbString;
    fRet = TRUE;

    Error:
    return fRet;
}


//
// Get the data out of the buffer
//    (the caller takes ownership of the m_szData storage)
//
LPSTR CBuffer::GiveUpData()
{
    // TBD_FUNCTION(CBuffer::GiveUpData);
    LPSTR szTemp = m_szData;

    m_szData = NULL;
    m_cbLength = 0;
    m_cbAlloc = 0;

    return szTemp;
}


//
//
//
void CBuffer::InheritData(CBuffer* pSrcBuffer)
{
    // TBD_FUNCTION(CBuffer::InheritData);
    delete[] m_szData;
    m_szData = NULL;
    m_cbLength = 0;
    m_cbAlloc = 0;

    m_cbAlloc = pSrcBuffer->m_cbAlloc;
    m_cbLength = pSrcBuffer->m_cbLength;
    m_szData = pSrcBuffer->GiveUpData();
}



//
// Shared resource ctor
//
CSharedResource::CSharedResource()
: m_fInited(FALSE),
m_dwSharedUsers(0),
m_hSharedUseSph(NULL),
m_hExclusiveUseSph(NULL),
m_hCancelEvent(NULL)
{
    TBD_FUNCTION(CSharedResource::CSharedResource);
    InitializeCriticalSection(&m_cs);
}


//
// Shared resource dtor
//
CSharedResource::~CSharedResource()
{
    TBD_FUNCTION(CSharedResource::~CSharedResource);
    if (m_hSharedUseSph)
    {
        (void)CloseHandle(m_hSharedUseSph);
        m_hSharedUseSph = NULL;
    }

    if (m_hExclusiveUseSph)
    {
        (void)CloseHandle(m_hExclusiveUseSph);
        m_hExclusiveUseSph = NULL;
    }

    DeleteCriticalSection(&m_cs);
    m_fInited = FALSE;
}


//
// Shared resource intialization
//
BOOL CSharedResource::Init(const HANDLE hCancelEvent)
{
    TBD_FUNCTION(CSharedResource::Init);
    if (m_fInited)
    {
        goto Error;
    }

    m_hSharedUseSph = CreateSemaphore(NULL, 1, 1, NULL);
    if (!m_hSharedUseSph)
    {
        goto Error;
    }

    m_hExclusiveUseSph = CreateSemaphore(NULL, 1, 1, NULL);
    if (!m_hExclusiveUseSph)
    {
        goto Error;
    }

    m_hCancelEvent = hCancelEvent;
    m_fInited = TRUE;

    Error:
    return m_fInited;
}


//
// Called to acquire shared use permission
//
BOOL CSharedResource::EnterSharedUse()
{
    TBD_FUNCTION(CSharedResource::EnterSharedUse);
    HANDLE rghObjects[2] = { m_hSharedUseSph, m_hCancelEvent};
    DWORD dwWait;
    BOOL fRet = FALSE;

    if (!m_fInited)
    {
        goto Error;
    }

    // Wait until shared use is granted
    dwWait = WaitForMultipleObjects(2, rghObjects, FALSE, INFINITE);
    if (WAIT_OBJECT_0 + 1 == dwWait)
    {
        // We hit the terminate event -- quit
        goto Error;
    }
    else if (WAIT_OBJECT_0 != dwWait)
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("WaitForMultipleObjects : Failed, RetVal=0x%x, GetLastError=0x%x"),dwWait,GetLastError());
        TBD_ASSERT(WAIT_FAILED == dwWait);
        goto Error;
    }

    {
        SYNCBLOCK(m_cs);

        // Grant shared use for anyone who's waiting
        if (!ReleaseSemaphore(m_hSharedUseSph, 1, NULL))
        {
            goto Error;
        }

        // Increment the number of shared users
        m_dwSharedUsers++;

        // If this is the first shared user, we need to make sure there are no exclusive users at the moment
        if (1 == m_dwSharedUsers)
        {
            // Wait until the exclusive user is done
            rghObjects[0] = m_hExclusiveUseSph;
            dwWait = WaitForMultipleObjects(2, rghObjects, FALSE, INFINITE);
            if (WAIT_OBJECT_0 + 1 == dwWait)
            {
                // We hit the terminate event -- quit
                goto Error;
            }
            else if (WAIT_OBJECT_0 != dwWait)
            {
                TBD_ASSERT(FALSE);
                goto Error;
            }
        }
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
// Called to relinquish shared use permission
//
BOOL CSharedResource::ExitSharedUse()
{
    TBD_FUNCTION(CSharedResource::ExitSharedUse);
    SYNCBLOCK(m_cs);

    BOOL fRet = FALSE;

    if (!m_fInited)
    {
        goto Error;
    }

    // Decrement the number of shared users
    m_dwSharedUsers--;

    // If this is the last shared user, allow exclusive use
    if (!m_dwSharedUsers)
    {
        if (!ReleaseSemaphore(m_hExclusiveUseSph, 1, NULL))
        {
            goto Error;
        }
    }

    fRet = TRUE;

    Error:
    return fRet;
}


//
// Called to acquire exclusive use permission
//
BOOL CSharedResource::EnterExclusiveUse(PFN_SHRDUSE_ACTION pfnAction, const DWORD dwParam) const
{
    TBD_FUNCTION(CSharedResource::EnterExclusiveUse);
    HANDLE rghObjects[2] = { m_hSharedUseSph, m_hCancelEvent};
    DWORD dwWait;
    BOOL fRet = FALSE;
    int RetryCount = 10;

    if (!m_fInited)
    {
        goto Error;
    }

    // Lock shared users out until this exclusive user can finish its operation
    dwWait = WaitForMultipleObjects(2, rghObjects, FALSE, INFINITE);
    if (WAIT_OBJECT_0 + 1 == dwWait)
    {
        // We hit the terminate event -- quit
        goto Error;
    }
    else if (WAIT_OBJECT_0 != dwWait)
    {
        TBD_ASSERT(FALSE);
        goto Error;
    }

    for (;;)
    {
        // Call the custom action routine, passing it the specified parameter
        pfnAction(dwParam);

        // Wait until there are no other exclusive users
        rghObjects[0] = m_hExclusiveUseSph;
        dwWait = WaitForMultipleObjects(2, rghObjects, FALSE, 500);
        if (WAIT_OBJECT_0 == dwWait)
        {
            // We own it now.
            break;
        }
        if (WAIT_OBJECT_0 + 1 == dwWait)
        {
            // We hit the terminate event -- quit
            goto Error;
        }
        else if (WAIT_TIMEOUT == dwWait)
        {
            // We timed out.
            // Have we exceeded our retry count?
            if (--RetryCount<=0)
            {
                goto Error;
            }
            // Try to signal and wait again.
        }
        else
        {
            // Some other error
            TBD_ASSERT(FALSE);
            goto Error;
        }
    }

    // Allow shared users to proceed (they will be locked out until there're no more exclusive users)
    if (!ReleaseSemaphore(m_hSharedUseSph, 1, NULL))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
// Called to relinquish exclusive use permission
//
BOOL CSharedResource::ExitExclusiveUse() const
{
    TBD_FUNCTION(CSharedResource::ExitExclusiveUse);
    // Allow other exclusive users and shared users to proceed
    return ReleaseSemaphore(m_hExclusiveUseSph, 1, NULL);
}

#ifdef RIL_USE_CPIN2_WORKAROUND
BYTE g_PIN2CRYPTBUFFER[256];
DWORD g_cbPIN2Bytes=0;

void ClearPIN2Secure()
{
    g_cbPIN2Bytes=0;
}

void SavePIN2Secure(LPCSTR szNewPIN)
{
    DATA_BLOB inBlob;
    inBlob.pbData = (BYTE*)szNewPIN;
    inBlob.cbData = strlen(szNewPIN) + 1;
    DATA_BLOB outBlob;
    outBlob.cbData = sizeof(g_PIN2CRYPTBUFFER);
    outBlob.pbData = g_PIN2CRYPTBUFFER;

    if ((szNewPIN == NULL) || (*szNewPIN == '\0') || (CryptProtectData(&inBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_SYSTEM, &outBlob) == FALSE))
    {
        g_cbPIN2Bytes = 0;
    }
    else
    {
        g_cbPIN2Bytes = outBlob.cbData;
    }
}

BOOL FetchPIN2Secure(LPSTR szPinBuffer, DWORD cbBufferSIze)
{
    DATA_BLOB inBlob;
    inBlob.cbData = g_cbPIN2Bytes;
    inBlob.pbData = g_PIN2CRYPTBUFFER;
    DATA_BLOB outBlob;
    outBlob.cbData = cbBufferSIze;
    outBlob.pbData = (BYTE *)szPinBuffer;
    BOOL fReturn = FALSE;

    if ((g_cbPIN2Bytes == 0) || (CryptUnprotectData(&inBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_SYSTEM, &outBlob) == FALSE))
    {
        szPinBuffer[0] = '\0';
    }
    else
    {
        fReturn = TRUE;
    }
    return fReturn;
}
#endif
